package hub

import (
	"encoding/json"
	"fmt"
	"log"
	"strconv"
	"sync"
	"time"
	"work-chess/chess"
	"work-chess/dao"
	"work-chess/model"
	"work-chess/service"

	"github.com/gorilla/websocket"
)

//CtlCenter 三层控制，CtlCenter控制组
type CtlCenter struct { //控制中心
	rw          sync.RWMutex
	ClientMap   map[*Group]bool
	CreateGroup chan *Group
	DisGroup    chan *Group
}

//Group 控制每个组里连接，以及群员之间消息的发送与接收
type Group struct {
	*CtlCenter
	*chess.Chessboard
	//红方是否准备
	RedReady uint
	//黑方是否准备
	BlackReady uint
	//第几回合
	Turn int
	//默认0，也可以是其他事件
	tag        int
	rw         sync.RWMutex
	ConnectMap map[*Connection]bool
	GroupId    int
	MsgChan    chan *Message
	Register   chan *Connection
	UnRegister chan *Connection
}

type Connection struct {
	*Group
	mu     sync.RWMutex
	wsConn *websocket.Conn
	User   string
	//Role是角色，红，黑，还是客人
	Role string
}

type Message struct {
	Time     string `json:"time"`
	UserName string `json:"user_name"`
	Type     string `json:"type"`
	Context  string `json:"context"`
}

var Center CtlCenter

func init() {
	Center.ClientMap = make(map[*Group]bool)
	Center.CreateGroup = make(chan *Group)
	Center.DisGroup = make(chan *Group)
	go Center.InitGroupHandle()
}

// 管理组的信息
func (ctl *CtlCenter) manageGroup() {
	for group := range ctl.ClientMap {
		if len(group.ConnectMap) == 0 {
			ctl.DisGroup <- group
			allWay := service.Range("room" + strconv.Itoa(group.GroupId))
			if len(allWay) != 0 {
				game := new(model.Game)
				for _, v := range allWay {
					way := new(model.Way)
					way.FromTo = v
					game.Way = append(game.Way, *way)
				}
				dao.SaveGame(*game)
			}
			service.RedisDEL("room" + strconv.Itoa(group.GroupId))
		}
		time.Sleep(10 * time.Second)
	}
}

//InitGroupHandle Ctl处理组的添加与销毁,这个应该在init里被调用
func (ctl *CtlCenter) InitGroupHandle() {
	for {
		select {
		case group := <-Center.CreateGroup: //创一个
			Center.ClientMap[group] = true
		case group := <-Center.DisGroup: //销毁一个
			delete(Center.ClientMap, group)
		}
	}
}

func (g *Group) InitConnHandle() { //处理每个组用户连接的handle，应在创建完一个组后被执行
	for {
		select {
		case conn := <-g.Register:
			conn.ConnectMap[conn] = true
		case conn := <-g.UnRegister:
			log.Println("删除连接" + strconv.Itoa(conn.GroupId) + ":" + conn.User)
			delete(conn.ConnectMap, conn)
		}
	}
}

// AddIntoGroup 将用户添加到某个组，有这个组就进入，没有就创建一个新群并进入,应该在创建连接是被调用
func (ctl *CtlCenter) AddIntoGroup(userName string, groupId int, conn *websocket.Conn) *Connection {
	//fmt.Println("添加新组:", groupId)
	exist := false
	for group := range ctl.ClientMap { //有这个组就加入这个组
		if groupId == group.GroupId {
			log.Println("用户" + userName + "进入" + strconv.Itoa(groupId))
			exist = true //这个群组是存在的\
			c := &Connection{
				Group:  group,
				wsConn: conn,
				User:   userName,
			}
			fmt.Println("group.ConnectMap", len(group.ConnectMap))
			if len(group.ConnectMap) == 0 {
				c.Role = "red"
			} else if len(group.ConnectMap) == 1 {
				for k := range group.ConnectMap {
					if k.Role == "black" {
						c.Role = "red"
					} else {
						c.Role = "black"
					}
				}
			} else {
				c.Role = "guest"
			}
			group.Register <- c
			return c
		}
	}
	if !exist { //这个组是不存在的情况
		group := &Group{ //先创建一个新组
			Chessboard: chess.NewGame(),
			CtlCenter:  ctl,
			Turn:       0,
			GroupId:    groupId,
			MsgChan:    make(chan *Message),
			ConnectMap: make(map[*Connection]bool),
			Register:   make(chan *Connection),
			UnRegister: make(chan *Connection),
		}
		log.Println("添加新组:", groupId)
		ctl.CreateGroup <- group
		go group.InitConnHandle() //启动管理这个组连接的协程(初始化组)
		//再将用户添加进组
		c := &Connection{
			Role:   "red",
			Group:  group,
			wsConn: conn,
			User:   userName,
		}
		group.Register <- c
		return c
	}
	return nil //额额
}

// Receive 对每一个连接，接受客户端发送的消息到服务器
func (conn *Connection) Receive() {
	//conn.wsConn.SetReadDeadline(time.Now().Add(60 * time.Second)) //60s的时间必须有一个来自客户端的消息
	defer func() {
		fmt.Println(recover())
		conn.Group.UnRegister <- conn
	}()
	for {

		var msgRes string
		var tag string
		err := conn.ResetDeadReadTime()
		_, msg, err := conn.wsConn.ReadMessage()
		//fmt.Println("err", err)
		//fmt.Println("receive", string(msg))
		if string(msg) == "pong" {
			//log.Println("收到消息：", string(msg))
			continue
		}
		if err != nil {
			fmt.Println(err)
		}

		if conn.tag == 0 {
			switch HandleMessageType(string(msg)) {
			case "ready":
				msgRes, tag = conn.handleReady()
			case "chat":
				tag = "all"
				msgRes = MessageContext(string(msg))
			case "chess":
				msgRes, tag = conn.handleChess(MessageContext(string(msg)))
				fmt.Println(msgRes)
			case "all":
				tag = "p"
				msgRes = conn.Chessboard.ChessboardRes()
			case "withdraw":
				msgRes = conn.handleWithdraw("please")
			}
		} else {
			msgRes = "有悔棋或其他事件，请先处理"
		}

		var message = Message{
			Type:     tag,
			UserName: conn.User,
			Context:  msgRes,
		}
		if err == nil {
			conn.MsgChan <- &message //将接收到的用户消息放进群组的msg通道
		}
	}
}

func (conn *Connection) Send() { //服务器发送消息到客户端
	//conn.wsConn.SetPongHandler()
	ticker := time.NewTicker(10 * time.Second)
	for {
		select {
		case msg := <-conn.MsgChan: //广播消息
			fmt.Println("send MsgChan")
			msg.Time = time.Now().String()
			err := conn.Broadcast(msg, websocket.TextMessage) //子类拥有父类的方法
			if err != nil {
				log.Println(err)
				return
			}
		case <-ticker.C: //ping，pong心跳消息，每30s发送一次，要求10s内写入conn，否则寄
			conn.mu.Lock()
			//conn.wsConn.SetWriteDeadline(time.Now().Add(10 * time.Second))
			//fmt.Println("开始设置ping消息给", conn.User)
			if err := conn.wsConn.WriteMessage(websocket.TextMessage, []byte("ping")); err != nil {
				log.Println("debug conn")
				//conn.mu.Unlock()
				return
			}
			conn.mu.Unlock()
			//fmt.Println("发送完一个ping")
		}
	}
}

// Broadcast 对一个组的每一个客户端发送用户消息
func (g *Group) Broadcast(msg *Message, msgType int) (err error) {
	for conn := range g.ConnectMap {
		fmt.Println("range if")
		if msg.Type == "all" || msg.Type == "system" || msg.Type == "" {
			marshal, _ := json.Marshal(msg)
			fmt.Println("发哦送:", string(marshal))
			g.rw.Lock()
			err = conn.wsConn.WriteMessage(msgType, marshal)
			g.rw.Unlock()
		} else if msg.Type == "p" {
			if msg.UserName == conn.User {
				marshal, _ := json.Marshal(msg)
				fmt.Println("发哦送:", string(marshal))
				g.rw.Lock()
				err = conn.wsConn.WriteMessage(msgType, marshal)
				g.rw.Unlock()
				break
			}
		}
	}
	return
}

func (g *Group) WhoTurn() string {
	if g.Turn%2 == 0 {
		return "red"
	}
	return "black"
}

func (g *Group) ReadyIf() bool {
	if g.RedReady == 1 && g.BlackReady == 1 {
		return true
	}
	return false
}

func (conn *Connection) ResetDeadReadTime() (err error) {
	//没有消息就阻塞，到了时间还没通就切断连接，并放行，让后面所有的read操作都会报错
	//在时间内收到消息就放行
	err = conn.wsConn.SetReadDeadline(time.Now().Add(20 * time.Second))
	return err
}

func (conn *Connection) ErrConn() {
	conn.wsConn.Close()
	conn.UnRegister <- conn
	log.Println(conn.User + "发生read message错误")
	return
}

func (m *Message) Encode() string {
	marshal, _ := json.Marshal(m)
	return string(marshal)
}

//func DeCode(s string) (m *Message) {
//	m = new(Message)
//	json.Unmarshal([]byte(s), m)
//	return m
//}

func (conn *Connection) Color() string {
	return conn.Role
}
